/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simplemetrosystem.view.canvas;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import simplemetrosystem.control.Calculation.CalculManager;
import simplemetrosystem.control.ElementManager;
import simplemetrosystem.control.EditCommandManager;
import simplemetrosystem.model.Element.Interface.CanvasElement;
import simplemetrosystem.model.Element.Interface.HighLightable;
import simplemetrosystem.model.Element.MetroStation;
import simplemetrosystem.model.Element.NullElement;
import simplemetrosystem.model.Element.Interface.Selectable;
import simplemetrosystem.model.Element.MetroString;
import simplemetrosystem.view.canvas.MouseHandlers.MouseTrans;

/**
 *
 * @author Eric Qian
 */
public class CanvasModel extends Observable implements Observer {

    /**
     * 
     * @return
     */
    public static CanvasModel getInstance() {
        return canvasModel;
    }

    /**
     * 
     * @param matrixSize
     * @param matrixWidth
     * @param matrixHeight
     * @return
     */
    public static CanvasModel newInstance(int matrixSize, int matrixWidth, int matrixHeight) {
        canvasModel = new CanvasModel(matrixSize, matrixWidth, matrixHeight);
        return canvasModel;
    }

    /**
     * 
     * @param matrixSize
     * @param matrixWidth
     * @param matrixHeight
     */
    public CanvasModel(int matrixSize, int matrixWidth, int matrixHeight) {
        super();
        this.matrixSize = matrixSize;
        this.matrixWidth = matrixWidth;
        this.matrixHeight = matrixHeight;
        MouseTrans.NewInstance(matrixSize);
        highLightElements = new ArrayList<HighLightable>();
        selectedElements = new Selectable[0];
        elementManager = ElementManager.getInstance();
        elementManager.addObserver(this);
        refrash();
    }

    /**
     * 
     */
    public void refrash() {
        elementManager = ElementManager.getInstance();
        matrix = new CanvasElement[matrixWidth][matrixHeight];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = new NullElement(i, j);
            }
        }

        for (int i = 0; i < elementManager.getStationsSize(); i++) {
            addElement(elementManager.getStationAt(i));
        }

        for (int i = 0; i < elementManager.getStringSize(); i++) {
            addElement(elementManager.getStringAt(i));
        }

        /**
        for (int i = 0; i < matrixWidth; i++) {
        for (int j = 0; j < matrixHeight; j++) {
        if (!(matrix[i][j] instanceof NullElement)) {
        MetroElement e = (MetroElement) matrix[i][j];
        System.out.println(e.getDisplayName() + " " + i + " " + j);
        }
        }
        }
         */
        updateAll();
    }

    /**
     * 
     * @param station
     */
    private void addElement(CanvasElement element) {
        int x1 = element.getLogX();
        int x2 = element.getLogX() + element.getWidth();
        int y1 = element.getLogY();
        int y2 = element.getLogY() + element.getHeight();
        for (int i = x1; i < x2; i++) {
            for (int j = y1; j < y2; j++) {
                matrix[i][j] = element;
            }
        }
    }

    /**
     * 
     * @param x
     * @param y
     * @param w
     * @param h
     */
    public void setHighLight(int x, int y, int w, int h) {
        for (int i = 0; i < highLightElements.size(); i++) {
            highLightElements.get(i).setHighLight(false);
            highLightElements.remove(i);
        }

        for (int i = 0; i <= w; i++) {
            for (int j = 0; j <= h; j++) {
                if ((matrix[x + i][y + j] instanceof HighLightable)) {
                    HighLightable aElement = (HighLightable) matrix[x + i][y + j];
                    if (!highLightElements.contains(aElement)) {
                        highLightElements.add(aElement);
                        aElement.setHighLight(true);
                    }
                }
            }
        }
        updateAll();
    }

    public void addCalSelected(int x, int y, int i) {
        if (selectedElements.length != 2) {
            selectedElements = new Selectable[2];
        }
        if (selectedElements[i] != null) {
            selectedElements[i].setSelected(false);
        }
        selectedElements[i] = (Selectable) matrix[x][y];
        selectedElements[i].setSelected(true);
    }

    /**
     * 
     */
    public void setSelect() {
        cancelSelect();
        selectedElements = new Selectable[highLightElements.size()];
        highLightElements.toArray(selectedElements);
        for (int i = 0; i < selectedElements.length; i++) {
            if (selectedElements[i] instanceof HighLightable) {
                HighLightable aElement = (HighLightable) selectedElements[i];
                aElement.setHighLight(false);
            }
            selectedElements[i].setSelected(true);
        }
        updateAll();
    }

    /**
     * 
     * @return
     */
    public boolean isSelectedEmpty() {
        return selectedElements.length == 0;
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public boolean isSelectedEmpty(int x, int y) {
        if (matrix[x][y] instanceof Selectable) {
            Selectable aElement = (Selectable) matrix[x][y];
            return !aElement.isSelected();
        }
        return true;
    }

    /**
     * 
     */
    public void cancelSelect() {
        for (int i = 0; i < selectedElements.length; i++) {
            selectedElements[i].setSelected(false);
        }
        selectedElements = new Selectable[0];
        updateAll();
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public boolean isEmpty(int x, int y) {
        return matrix[x][y] instanceof NullElement;
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public boolean isStationAvailable(int x, int y) {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 2; j++) {
                if (!(matrix[x + i][y + j] instanceof NullElement)) {
                    boolean test = true;
                    for (int k = 0; k < selectedElements.length; k++) {
                        if (selectedElements[k] == matrix[x + i][y + j]) {
                            test = false;
                        }
                    }
                    if (test) {
                        return false;
                    }
                }
            }
        }
        if (x != 0) {
            int i = -1;
            for (int j = 0; j < 2; j++) {
                if (!(matrix[x + i][y + j] instanceof NullElement)) {
                    boolean test = false;
                    for (int k = 0; k < selectedElements.length; k++) {
                        if (selectedElements[k] == matrix[x + i][y + j]) {
                            test = true;
                        }
                        System.out.println(matrix[x + i][y + j]);
                    }
                    if (test) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public boolean isStation(int x, int y) {
        return matrix[x][y] instanceof MetroStation;
    }

    /**
     * 
     * @param x
     * @param y
     */
    public void clear(int x, int y) {
        if (matrix[x][y] instanceof MetroStation) {
            EditCommandManager.getInstance().removeStation((MetroStation) matrix[x][y]);
        } else if (matrix[x][y] instanceof MetroString) {
            EditCommandManager.getInstance().removeString((MetroString) matrix[x][y]);
        }

    }

    public void clearSelected() {
        for (int i = 0; i < selectedElements.length; i++) {
            selectedElements[i].setSelected(false);
        }
        selectedElements = new Selectable[0];
    }

    /**
     * 
     * @param x
     * @param y
     * @return
     */
    public CanvasElement getCanvasElement(int x, int y) {
        return matrix[x][y];
    }

    /**
     * 
     * @param i
     * @return
     */
    public Selectable getSelectedElementAt(int i) {
        return selectedElements[i];
    }

    /**
     * 
     * @return
     */
    public int getSelectedElementSize() {
        return selectedElements.length;
    }

    /**
     * 
     * @param matrixSize
     */
    public void setMatrixSize(int matrixSize) {
        this.matrixSize = matrixSize;
        updateAll();
    }

    /**
     * 
     * @return
     */
    public int getMatrixHeight() {
        return matrixHeight;
    }

    /**
     * 
     * @return
     */
    public int getMatrixWidth() {
        return matrixWidth;
    }

    /**
     * 
     * @return
     */
    public int getMatrixSize() {
        return matrixSize;
    }

    public void updateAll() {
        setChanged();
        notifyObservers();
    }
    private static CanvasModel canvasModel;
    private CanvasElement[][] matrix;
    private ElementManager elementManager;
    private Selectable[] selectedElements;
    private ArrayList<HighLightable> highLightElements;
    private int matrixSize;
    private int matrixWidth;
    private int matrixHeight;

    public void update(Observable o, Object arg) {
        if (o instanceof ElementManager) {
            refrash();
        }
    }
}